package org.evolAI.organism;

import java.util.Collection;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.evolAI.organism.domain.Organism;

public class Population {
	
	private static final Logger logger = Logger.getLogger(Population.class);
	
	private ExecutorService mutateExecutor;
	
	private ConcurrentHashMap<UUID, Organism> pop = new ConcurrentHashMap<UUID, Organism>();
	
	public Population() {
		mutateExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
	}
	
	public Organism get(UUID id) {
		return pop.get(id);
	}
	
	public void add(Organism org) {
		pop.put(org.getId(), org);
	}
	
	public Collection<Organism> values() {
		return pop.values();
	}
	
	public void clear() {
		pop.clear();
	}
	
	public void exec(String message, int maxDepth, int maxSteps) {
		for (Organism org: pop.values()) {
			org.sendMessage(message);
			org.live(maxDepth, maxSteps);
		}
	}
	
	public void execSelected(String message, int maxDepth, int maxSteps) {
		for (Organism org: pop.values()) {
			if (org.isSelected()) {
				org.sendMessage(message);
				org.live(maxDepth, maxSteps);
			}
		}
	}

	private void cleanup() {
		for (final Organism org: pop.values()) {
			if (!org.isSelected() || org.isDeleted()) {
				pop.remove(org.getId());
			}
		}
	}
	
	public void mutate(final String message, final int generations, final int offspring, final int maxDepth, final int maxSteps) {
		cleanup();
		for (final Organism org: pop.values()) {
			if (org.isSelected() && !org.isDeleted()) {
				saveFamily(org);
				mutateOrg(org, message, offspring, generations, maxDepth, maxSteps);
			}
		}
		try {
			mutateExecutor.awaitTermination(120, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	private void mutateOrg(final Organism org, final String message, final int offspring, final int generation, final int maxDepth, final int maxSteps) {
		if (generation == 0)
			return;
				
		for (int i = 0; i < offspring; i++) {
			mutateExecutor.execute(
				new Runnable() {
					public void run() {
						Organism newOrg = org.copy();
			        	newOrg.mutate();
						newOrg.sendMessage(message);
						newOrg.live(maxDepth, maxSteps);
						if (!newOrg.getResponses().trim().equals("") && !newOrg.isStop()) {
			            	add(newOrg);
			            	mutateOrg(newOrg, message, offspring, generation-1, maxDepth, maxSteps);
			            }
					}
				});
			
		}
	}
	
	private void saveFamily(Organism org) {
		org.save("organisms\\" + org.getId().toString() + ".organism");
		Organism pOrg = org;
		do {
			pOrg = pOrg.getParent();
			if (pOrg == null)
				break;
			pOrg.save("organisms\\" + pOrg.getId().toString() + ".organism");
		} while (true);
	}
}
